package app;

import java.io.IOException;
import java.util.Map;
import java.util.Random;

import constant.Constant;
import entity.ActionPoint;
import entity.Environment;
import entity.NextState;
import entity.QTable;
import entity.Step;
import enumeration.Action;
import enumeration.R;

public class App {

    static Random random = new Random();

    public static void main(String[] args) throws InterruptedException, IOException {
        int winCount = 0;
        int loseCount = 0;
        Runtime runtime = Runtime.getRuntime();
        App app = new App();
        Environment env = new Environment();
        Step nextStep = new Step();
        for (int i = 0; i < Constant.PLAY_COUNT; i++) {
            int stepCount = 0;
            env.init();
            app.show(Environment.getEnv());
            R r = R.NORMAL;

            Action[] actions = Action.values();
            Step step = new Step();
            step.setStep(0);
            while (r == R.NORMAL) {
                stepCount += 1;
                System.out.println();
                // 选择下一步
                Map<Step, ActionPoint> qTableMap = QTable.getqTableMap();
                ActionPoint stepActionPoint = qTableMap.get(step);
                Action stepAction;
                if (Math.random() > Constant.CHOOSE_RATE || stepActionPoint == null) {
                    stepAction = actions[random.nextInt(actions.length)];
                } else {
                    stepAction = stepActionPoint.rightAction();
                }
                // 更新q_table
                if (stepActionPoint == null) {
                    stepActionPoint = new ActionPoint();
                    Step newStep = new Step();
                    newStep.setStep(step.getStep());
                    qTableMap.put(newStep, stepActionPoint);
                }
                NextState updateEnv = env.updateEnv(step, stepAction);
                nextStep.setStep(updateEnv.getNextStep());
                r = updateEnv.getR();
                ActionPoint nextActionPoint = qTableMap.get(nextStep);
                if (nextActionPoint == null) {
                    nextActionPoint = new ActionPoint();
                    Step newNextStep = new Step();
                    newNextStep.setStep(nextStep.getStep());
                    qTableMap.put(newNextStep, nextActionPoint);
                }
                app.updateActionPoint(stepActionPoint, nextActionPoint, r, stepAction);

                step = new Step();
                step.setStep(nextStep.getStep());
                app.show(Environment.getEnv());
                Thread.sleep(Constant.TIME);
                runtime.exec("clear");
            }
            if (r == R.WIN) {
                winCount += 1;
            } else {
                loseCount += 1;
            }
            System.out.println();
            System.out.println("次数：" + i + "\t单次步数：" + stepCount + "\t是否成功：" + (r == R.WIN ? "成功" : "失败"));
            System.out.println("成功次数：" + winCount + "\t失败次数：" + loseCount);
            System.out.println();
            Thread.sleep(Constant.SHOW_TIME);
            runtime.exec("clear");

        }
        Map<Step, ActionPoint> getqTableMap = QTable.getqTableMap();
        for (Map.Entry<Step, ActionPoint> table : getqTableMap.entrySet()) {
            ActionPoint value = table.getValue();
            System.out.println(table.getKey().getStep() + " " + value.getLeft() + " " + value.getRight() + " " + value.getUp() + " " + value.getDown());
        }
    }

    public void show(int[] env) {
        for (int i = 0; i < env.length; i++) {
            if (i != 0 && i % Constant.WIDTH == 0) {
                System.out.println();
            }
            if (env[i] == Constant.WIN_POINT) {
                System.out.print("T ");
            } else if (env[i] == Constant.LOSE_POINT) {
                System.out.print("X ");
            } else if (env[i] == Constant.PLAYER_POINT) {
                System.out.print("o ");
            } else {
                System.out.print("- ");
            }
        }
    }

    public void updateActionPoint(ActionPoint actionPoint, ActionPoint nextActionPoint, R r, Action action) {
        double targetPredict = 0;
        switch (r) {
            case WIN:
                targetPredict = Constant.WIN_SOCRE;
                break;
            case LOSE:
                targetPredict = Constant.LOSE_SOCRE;
                break;
            case NORMAL:
                targetPredict = Constant.NORMAL_SOCRE + Constant.GAMMA * nextActionPoint.max();
                break;
        }
        switch (action) {
            case LEFT:
                actionPoint.setLeft(actionPoint.getLeft() + Constant.ALPHA * (targetPredict - actionPoint.getLeft()));
                break;
            case RIGHT:
                actionPoint.setRight(actionPoint.getRight() + Constant.ALPHA * (targetPredict - actionPoint.getRight()));
                break;
            case UP:
                actionPoint.setUp(actionPoint.getUp() + Constant.ALPHA * (targetPredict - actionPoint.getUp()));
                break;
            case DOWN:
                actionPoint.setDown(actionPoint.getDown() + Constant.ALPHA * (targetPredict - actionPoint.getDown()));
                break;
        }
    }
}
